import { reactive, computed, ref } from 'vue';

function useSwipe(target, options) {
  const {
    threshold = 50,
    onSwipe,
    onSwipeEnd,
    onSwipeStart
  } = options;
  const posStart = reactive({
    x: 0,
    y: 0
  });
  const updatePosStart = (x, y) => {
    posStart.x = x;
    posStart.y = y;
  };
  const posEnd = reactive({
    x: 0,
    y: 0
  });
  const updatePosEnd = (x, y) => {
    posEnd.x = x;
    posEnd.y = y;
  };
  const distanceX = computed(() => posStart.x - posEnd.x);
  const distanceY = computed(() => posStart.y - posEnd.y);
  const {
    max,
    abs
  } = Math;
  const isThresholdExceeded = computed(() => max(abs(distanceX.value), abs(distanceY.value)) >= threshold);
  const isSwiping = ref(false);
  const isPointerDown = ref(false);
  const duration = ref(0);
  let startT;
  const direction = computed(() => {
    if (!isThresholdExceeded.value) return 'none';
    if (abs(distanceX.value) > abs(distanceY.value)) {
      return distanceX.value > 0 ? 'left' : 'right';
    }
    return distanceY.value > 0 ? 'up' : 'down';
  });
  const eventIsAllowed = e => {
    const isReleasingButton = e.buttons === 0;
    const isPrimaryButton = e.buttons === 1;
    return options.pointerTypes?.includes(e.pointerType) ?? (isReleasingButton || isPrimaryButton) ?? true;
  };
  const handlePointerDown = e => {
    if (!eventIsAllowed(e)) return;
    e.preventDefault();
    isPointerDown.value = true;
    startT = performance.now();

    // Disable scroll on for TouchEvents
    target?.style?.setProperty('touch-action', 'none');
    // Future pointer events will be retargeted to target until pointerup/cancel
    const eventTarget = e.target;
    eventTarget?.setPointerCapture(e.pointerId);
    const {
      clientX: x,
      clientY: y
    } = e;
    updatePosStart(x, y);
    updatePosEnd(x, y);
    onSwipeStart?.(e);
  };
  const handlePointerMove = e => {
    if (!eventIsAllowed(e)) return;
    if (!isPointerDown.value) return;
    const {
      clientX: x,
      clientY: y
    } = e;
    updatePosEnd(x, y);
    if (!isSwiping.value && isThresholdExceeded.value) isSwiping.value = true;
    if (isSwiping.value) onSwipe?.(e);
  };
  const handlePointerUp = e => {
    if (!eventIsAllowed(e)) return;
    const durationVal = performance.now() - startT;
    duration.value = durationVal;
    if (isSwiping.value) onSwipeEnd?.(e, direction.value, durationVal);
    isPointerDown.value = false;
    isSwiping.value = false;
    target?.style?.setProperty('touch-action', 'initial');
  };
  target.addEventListener('pointerdown', handlePointerDown);
  target.addEventListener('pointermove', handlePointerMove);
  target.addEventListener('pointerup', handlePointerUp);
  const stop = () => {
    target.removeEventListener('pointerdown', handlePointerDown);
    target.removeEventListener('pointermove', handlePointerMove);
    target.removeEventListener('pointerup', handlePointerUp);
  };
  return {
    isSwiping,
    direction,
    posStart,
    posEnd,
    distanceX,
    distanceY,
    duration,
    stop
  };
}

export { useSwipe };
